iT邦幫忙

2026 iThome 鐵人賽

DAY 2
0

01. 只想把模型接上去

接模型時,很容易把成功想成一件簡單的事:送出請求,收到回覆。這次「從心出發」確實收到了 HTTP 200,也就是網路介面回報請求成功的狀態碼。但最後留下的整合結果,仍然是 BLOCKED:必要條件不足,不能放行。

卡住的地方很小,只有回應裡的模型名稱。

Day 01,我用工程日誌約束自己能宣稱什麼;這一篇,同樣的問題落到了真正的模型介面上。本篇整理接線與後續身分查核的既有紀錄,並非同一天新做完的實驗;為了寫文章,我沒有重新呼叫模型。

這次要接的是大型語言模型(LLM),負責在允許的一般支持情境中產生文字。程式已把模型供應端(provider)抽成獨立介面,再用轉接器(adapter)處理送往校方模型閘道的請求。閘道是代為轉送模型請求的服務;把它隔開,對話流程就不必自己處理每一種網路錯誤。

但「已寫好轉接器」離「已驗證接上指定模型」,還有一段距離。

02. HTTP 200 出現了

最初的驗證缺少必要設定,因此沒有送出請求。後來設定到位,授權驗證送出了 1 次最小推論請求,也就是只請模型產生少量測試文字,不送真實使用者對話。

那次回覆是 HTTP 200,JSON 也可解析。JSON 是介面交換結構化資料的文字格式;能解析,只表示程式讀得懂資料,不表示內容符合期待。報告裡的 responseModelMatchesfalse:模型名稱沒有符合要求,轉接器回報 SCHEMA_ERROR

這份最初的紀錄沒有留下回傳名稱原字串,我不能用後來看到的值替它補答案。

後續身分查核另送了 1 次最小推論請求,才保存下列對照:

欄位 實際紀錄
請求的模型名稱 gpt-5.6-luna
回應中的模型名稱 gpt-5.6-luna-2026-07-09
HTTP 狀態 200
JSON 解析 通過
名稱完全相同
轉接器驗收 FAIL/SCHEMA_ERROR

兩次推論都沒有重試。這不是同一筆回覆的不同寫法,也不是「多試幾次,總有一次會過」。

03. 但系統沒有讓我過

Schema 是資料必須遵守的欄位與型別規則。在目前實作中,模型身分也被放進回應契約,因此 SCHEMA_ERROR 不只可能代表少了欄位;名稱不符,同樣會被拒絕。

轉接器在解析 JSON 後,先做這個判斷:

if payload["model"] != config.model:
    raise ValueError

這是現有程式的一小段。周圍的錯誤處理會把它轉為 SCHEMA_ERROR。只有名稱相同,才繼續檢查候選回覆、結束原因、訊息角色與文字內容;這次在前面就被擋住,不能宣稱完整 schema 已通過。

下面依現有程式簡化流程,省略設定、逾時與大小限制的細項,以及所有回覆共用的最後檢查。它是工作樹實作的說明,不是已部署的架構圖。

flowchart TD
    A[輸入先經規則分流] --> B{允許一般生成?}
    B -- 否 --> C[危機或受限分支:固定回覆]
    B -- 是 --> D[向設定的 provider 送出請求]
    D --> E{HTTP 與回應格式檢查通過?}
    E -- 否 --> X[拒絕模型輸出,使用固定備援]
    E -- 是 --> F{JSON 可解析?}
    F -- 否 --> X
    F -- 是 --> G{model 與設定完全相同?}
    G -- 否 --> X
    G -- 是 --> H{其餘回應欄位合格?}
    H -- 否 --> X
    H -- 是 --> I{生成文字通過安全檢查?}
    I -- 否 --> X
    I -- 是 --> J[保留生成文字供最後檢查]

圖裡沒有「別名自動辨識」這個步驟,因為現行轉接器沒有實作它。模型回覆通過介面契約,也還要經過輸出安全檢查,不能直接交給使用者。

04. 「差不多是同一個模型」為什麼不能算證明

看到名稱多了日期,很容易提出一個假設:這會不會只是指定版本?模型清單裡又出現 azure/gpt-5.6-luna,看起來也很接近。

但這裡有三種不同用途的名稱:送出請求用的名字、生成回應宣告的名字,以及模型清單列出的名字。它們相似,不代表已經建立對應關係。

Alias 是別名;canonical identity 則是在契約中用來明確指認模型的正式身分。要接受別名,缺的不是刪掉日期的程式,而是有權說明這個關係的來源:例如供應方文件,或經獨立審查、明確標示版本與適用範圍的閘道對照資料。

後續查核保留了更完整的模型清單,但仍沒有取得能證明等價的資料。所以我的結論是:「我沒有證據證明它們相同。」這也不等於已經證明它們是不同模型。

紀錄將它分類為 UNPROVEN_VARIANT,意思是觀察到不同名稱,等價性尚未證明。這裡核對的是閘道宣告是否符合契約,並沒有直接驗證底層實際運行的模型;即使名稱完全相同,也不能單靠字串比較證明後端的一切。

05. Fail Closed:停的是沒有證據的放行

Fail-open 是條件無法確認時仍讓流程繼續;fail-closed 則是在必要條件不成立時拒絕放行。這次如果改成「名稱有共同開頭就接受」,其實就是把尚未證明的假設寫進允許條件。

目前沒有這樣改。執行時,轉接器拒絕不符契約的回應;如果一般支持流程遇到這類錯誤,就使用明確標示的固定備援文字。整合驗收則保留 BLOCKED。前者是使用者會走到的回覆路徑,後者是工程上不能宣布完成的狀態。

這件事回到「從心出發」,不只是回答品質問題。我不能假定名稱接近的模型,在能力與限制上就可以互換;否則部署時也說不清楚,之前的檢查究竟適用於哪個供應端。

心理支持還需要更早的邊界。危機分流(crisis routing)是先依規則判斷是否進入固定支持路徑,不把是否繼續生成交給模型。現有程式對危機、高度困擾與未知風險都會避開一般模型呼叫。既有查核中的 23 個人工編寫的合成危機案例,全部走固定危機支持分支,provider 呼叫、傳輸嘗試及網路呼叫都為 0;這是有限測試案例的結果,本次沒有重跑,也不能推成所有危機都能辨識。

模型身分契約不能替代安全政策,安全政策也不能替身分不明背書。「從心出發」仍是非臨床工程專案,不宣稱診斷、治療或取代專業心理師;現有規則式檢查也不是任意文字的安全保證。

06. 今天真正完成了什麼

最後一輪查核把「什麼證據才足以接受別名」寫成政策,並建立隔離的 QA 契約驗證器。QA 指品質驗證;這個工具用來測試契約會不會錯放行,尚未接進正式執行流程。

它有 42 個受控案例通過,另有 2 個既有轉接器模擬測試方法通過。這些正向案例使用合成來源,只能證明測試條件下的驗證行為,不能替真實模型建立別名關係。實際待解契約仍是 active=false,可接受名稱清單為空。

以下都是本篇採用的既有紀錄,沒有在寫作時重新執行。其中回歸測試,是檢查原有功能是否受到改動影響:

項目 結果
對話重點測試/安全測試/回歸測試 87/24/160 項,原報告皆 PASS;範圍重疊,不能相加
最小推論的介面回覆 曾收到 HTTP 200,但轉接器驗收 FAIL
模型名稱等價關係 UNKNOWN,尚無權威對照證據
最新身分查核 新增模型清單請求 0、推論請求 0;維持 BLOCKED
別名契約 隔離 QA 設計存在;未啟用,未接入正式執行流程

最後沒有再送請求,是因為缺的證據已經變了:另一段生成文字,不能回答誰有權確認別名關係。

07. 今天沒有完成什麼

一般心理支持情境的真實模型驗收仍是 NOT_RUN,也就是沒有執行;不能用模擬測試通過來填補。真實生成能力仍記為未驗證,模型等價性也沒有確認。

最後一輪身分查核沒有重跑完整測試與危機測試,只沿用已保存的證據;本次寫作也沒有重跑這些測試。相關對話實作與驗證成果尚未形成新的 Git commit(版本提交),也沒有在這些工作中部署。外部測試環境與正式環境的現況,本篇未查驗。

08. 我今天學到的事

  • 成功要帶著範圍說:網路回覆、資料解析、契約驗收與正式啟用,是不同問題。
  • 不確定性要留在結果裡。UNPROVEN_VARIANT 比猜成「同一個」或「錯的模型」更準確。
  • 保存證據也要克制。模型名稱與拒絕原因有助於追查,不需要為此把使用者心理內容或完整回覆一起留下。
  • 測試通過的是被測條件;用合成資料驗證一個別名契約,不會讓真實別名自動成立。

09. Day 03:誰有權替這個名稱作證?

下一個候選題目,是取得並審查一份真正有來源的模型對照資料:由誰發布、適用於哪個部署、何時生效,以及能否支持已保存的那次回應。這是待做的工作,不是已拿到的文件。

即使資料到位,也還得另外審查如何接入執行流程。我要補的是可驗證的依據,才能讓下一次「成功」有明確的意思。


上一篇
# 從心出發開發日誌|在讓 AI 開口安慰人以前,我先決定它什麼時候不能說話
下一篇
# Day 03|讓心理 AI 記得、撤回、恢復,然後才談介入
系列文
《從心出發:30 天打造一套具 RAG、心理支持決策、安全治理與 ARCI 自適應能力的 AI 心理支持平台》6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言